package com.ruan.lkx.address;

import com.ruan.lkx.utils.Base58;
import com.ruan.lkx.utils.Hash;

import javax.print.DocFlavor;
import java.math.BigInteger;
import java.security.*;
import java.security.interfaces.ECPublicKey;
import java.security.spec.ECGenParameterSpec;
import java.security.spec.ECPoint;
import java.sql.Array;
import java.time.Year;
import java.util.Arrays;

/**
 * @author likaixin
 * @ClassName com.ruan.lkx.address.Address
 * @create 2021年12月20日 20:51
 * @desc:该类用于生成比特币的地址
 */
public class Address {



    /*
    步骤
    *生成一个私钥
    *得到对应的公钥
    *对公钥进行哈希计算(a,sha256,b,ripemd160)
    *拼接版本号前缀:ver+pubhash
    *双重哈希,去效验位:check
    *拼接校验位到4结果后
    *base58编码后,得到字符串addr
     */
    /**
     * java中的原生,生成一对密钥,并将密钥对对象KeyPair返回
     * @return 生成的密钥对
     */
    public KeyPair generateKeys(){
        try{
            KeyPairGenerator generator = KeyPairGenerator.getInstance("EC");
            ECGenParameterSpec spec  = new ECGenParameterSpec("secp256k1");
            generator.initialize(spec);

            return generator.generateKeyPair();
        }catch (NoSuchAlgorithmException e){
            e.printStackTrace();
        }catch (InvalidAlgorithmParameterException e){
            e.printStackTrace();
        }
        return null;


    }

    /**
     *  根据提供的密钥对数据,推导生成的一个原始格式的比特币地址,并返回
     * @param keyPair 提供的密钥对信息
     * @return 推导生成的比特币地址
     */
    public String newAddress(KeyPair keyPair){
       if (keyPair==null){
        return null;
       }
        ECPublicKey  publicKey =(ECPublicKey) keyPair.getPublic();
        //原始格式的公钥:04+x坐标(32字节)+y坐标(32字节)
        ECPoint point = publicKey.getW();
        byte[] xBytes=point.getAffineX().toByteArray();
        byte[] yBytes = point.getAffineY().toByteArray();
        System.out.println(xBytes.length);//查看x坐标长度 32
        System.out.println(yBytes.length);//查看y坐标长度 32
        //32=>满足条件 全部拿来用
        //33=>不满足条件 0[1,32]
        byte[] pubKey = toUncompressedPubKey(xBytes, yBytes);
        System.out.println(pubKey.length);
        byte[] sha256 = Hash.SHA256(pubKey);
        //公钥哈希
        byte[] pubHash=Hash.RIPMD160(sha256);
        //添加前缀
        byte[] version= new byte[1];
        version[0]=0x00;
        byte[] ver_pubhash=new byte[version.length+pubHash.length];
        System.arraycopy(version,0,ver_pubhash,0,version.length);
        System.arraycopy(pubHash,0,ver_pubhash,1,pubHash.length);
        //双哈希,去效验位
        byte[] hash1= Hash.SHA256(ver_pubhash);
        byte[] hash2=Hash.SHA256(hash1);
        byte[] check=new byte[4];
        System.arraycopy(hash2,0,check,0,4);


        byte[] ver_pubhash_check=new byte[ver_pubhash.length+check.length];
        System.arraycopy(ver_pubhash,0,ver_pubhash_check,0,ver_pubhash.length);
        System.arraycopy(check,0,ver_pubhash_check,ver_pubhash.length,check.length);
        //base58编码

        return Base58.encode(ver_pubhash_check);
    }

    /**
     * 该方法根据x坐标和y坐标数据,用于拼接生成原始格式的公钥
     * @param x
     * @param y
     * @return 原始格式的公钥
     */
    private static byte[] toUncompressedPubKey(byte[] x,byte[] y){
        //原始格式公钥:04+x坐标(32字节)+y坐标(32)字节
         byte[] publicKey=new byte[65];
         publicKey[0]=04;
         if (x.length==32){
             //将x坐标全部拷贝到创建的原始格式公钥中去
             System.arraycopy(x,0,publicKey,1,x.length);
         }else if (x.length>32){
            System.arraycopy(x,1,publicKey,1,32);
         }
            if (y.length==32){
                System.arraycopy(y,0,publicKey,33,y.length);
            }else if(y.length>32){
                System.arraycopy(y,1,publicKey,33,32);
            }


        return publicKey;

    }

    /**
     * 效验比特币地址是否符合规范,并返回效验结果
     * @param addr 要效验的字符串
     * @return 效验结果true表示复合规范,false表示不符合规范
     */
    public boolean validateAddr(String addr){
        if ("".equals(addr)||addr==null){
            return false;
        }
        byte[] ver_pubhash_check=Base58.decode(addr);
        if (ver_pubhash_check.length<4){
            return false;
        }
        byte[] check=new byte[4];
        System.arraycopy(ver_pubhash_check,ver_pubhash_check.length-4,check,0,4);
        byte[] ver_pubhash=new byte[ver_pubhash_check.length-4];
        System.arraycopy(ver_pubhash_check,0,ver_pubhash,0,ver_pubhash.length);
        //双hash,取前4个字节
        byte[] hash1 = Hash.SHA256(ver_pubhash);
        byte[] hash2 = Hash.SHA256(hash1);
        if (hash2==null){
            return false;
        }
        byte[] check_sum=new byte[4];
        System.arraycopy(hash2,0,check_sum,0,check_sum.length);

        //比较两个字节数组是否相等
        return Arrays.equals(check,check_sum);



    }
    //临时测试
//    public static void main(String[] args) {
//        Address addr = new Address();
//       KeyPair keyPair = addr.generateKeys();
//        String address = addr.newAddress(keyPair);
//        boolean b = addr.validateAddr(address);
//        System.out.println(b);
//
//    }
}

